package com.hr.ping.service;

import com.hr.ping.util.FileLockUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
import reactor.util.retry.Retry;

import java.io.IOException;
import java.net.ConnectException;
import java.nio.channels.FileLock;
import java.time.Duration;

@Service
public class PingService {

    private static final Logger log = LoggerFactory.getLogger(PingService.class);
    private final WebClient webClient;
    private final FileLockUtil fileLockUtil;

    public PingService(WebClient webClient, FileLockUtil fileLockUtil) {
        this.webClient = webClient;
        this.fileLockUtil = fileLockUtil;
    }

    public Mono<String> ping() {
        return fileLockUtil.acquireRateLimitLock().flatMap(
            lock -> webClient.method(HttpMethod.GET).uri("/pong").accept(MediaType.TEXT_PLAIN)
            .retrieve().bodyToMono(String.class)
            .doOnSuccess(response->{
                log.info("Request sent & Pong responded: {}, threadID:{}", response, Thread.currentThread().getId());
                releaseLock(lock);
            })
             .doOnError(error -> {
                 log.error("Error while sending request to Pong", error);
                 releaseLock(lock);
             })
        ).onErrorResume(error->{
            if (error instanceof ConnectException) {
                log.error("Connection refused: {}", error.getMessage());
                return Mono.error(new RuntimeException("Connection refused"));
            }
            return Mono.error(error);
        }).retryWhen(Retry.backoff(3, Duration.ofSeconds(2))
                          .filter(throwable -> throwable instanceof ConnectException));
    }

    private void releaseLock(FileLock lock) {
        if (lock != null && lock.isValid()) {
            try {
                lock.release();
            } catch (IOException e) {
                log.error("Failed to release file lock", e);
            }
        }
    }

}
